/*
 * Copyright (C) 2021 HiHope Open Source Organization .
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *
 * limitations under the License.
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#include <stdio.h>
#include "cmsis_os2.h"
#include "ohos_run.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/i2c.h"

#include "max30102.h"

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
#endif

#define MS_PER_S 1000

#define GPIO_KEY 4

#define __cnt__500 500
#define __cnt__400 400
#define __cnt__100 100

uint32_t aun_ir_buffer[500];  // IR LED sensor data
int32_t n_ir_buffer_length;   // data length
uint32_t aun_red_buffer[500]; // Red LED sensor data
int32_t n_sp02;               // SPO2 value
int8_t ch_spo2_valid;         // indicator to show if the SP02 calculation is valid
int32_t n_heart_rate;         // heart rate value
int8_t ch_hr_valid;           // indicator to show if the heart rate calculation is valid
uint8_t uch_dummy;

uint32_t un_min, un_max, un_prev_data;
int i;
int32_t n_brightness;
float f_temp;
uint8_t temp_num = 0;
uint8_t temo_buf[6];
uint8_t str[100];
uint8_t dis_hr = 0, dis_spo2 = 0;

uint8_t f_dis_hr = 0, f_dis_spo2 = 0;

#define MAX_BRIGHTNESS 255

static void EnvironmentTask(void *arg)
{
    (void)arg;
    uint8_t intkey_val = 0;
    uint32_t retval = 0;
    float humidity = 0.0f;
    float temperature = 0.0f;
    static char line[32] = {0};

    i2c_config_t i2c_initer = {
        .clk_flags = 0,            // 选择默认时钟源
        .master.clk_speed = 50000, // 指定速率为100Kbit,最大可以为400Kbit
        .mode = I2C_MODE_MASTER,   // 主机模式
        .scl_io_num = 22,          // 指定SCL的GPIO口
        .scl_pullup_en = true,     // SCL接上拉电阻
        .sda_io_num = 21,          // 指定SDA的GPIO口
        .sda_pullup_en = true,     // SDA接上拉电阻
    };
    if (i2c_param_config(I2C_NUM_0, &i2c_initer) == ESP_OK)
    {
        printf("i2c parm config success\r\n");
    }
    else
    {
        printf("config fail\r\n");
    }

    if (i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0) == ESP_OK)
    {
        printf("i2c driver install success\r\n");
    }
    else
    {
        printf("driver fail\r\n");
    }

    // 配置GPIO为输入
    gpio_config_t gpio_two = {
        .intr_type = GPIO_INTR_DISABLE,
        .mode = GPIO_MODE_INPUT,
        .pin_bit_mask = 1ULL << GPIO_KEY,
        .pull_down_en = GPIO_PULLDOWN_ENABLE,
        .pull_up_en = GPIO_PULLUP_DISABLE};

    gpio_config(&gpio_two);

    max30102_init();

    printf("\r\n MAX30102  init  \r\n");

    un_min = 0x3FFFF;
    un_max = 0;

    n_ir_buffer_length = 500; // buffer length of 100 stores 5 seconds of samples running at 100sps
    // read the first 500 samples, and determine the signal range
    for (i = 0; i < n_ir_buffer_length; i++)
    {
        while (1) // wait until the interrupt pin asserts
        {
            intkey_val = gpio_get_level(GPIO_KEY); // IoTGpioGetInputVal(5, &intkey_val);
            if (intkey_val == 0)
            {
                break;
            }
            usleep(10);
        }

        max30102_FIFO_ReadBytes(REG_FIFO_DATA, temo_buf, 6);
        aun_red_buffer[i] = (long)((long)((long)temo_buf[0] & 0x03) << 16) | (long)temo_buf[1] << 8 | (long)temo_buf[2]; // Combine values to get the actual number
        aun_ir_buffer[i] = (long)((long)((long)temo_buf[3] & 0x03) << 16) | (long)temo_buf[4] << 8 | (long)temo_buf[5];  // Combine values to get the actual number

        if (un_min > aun_red_buffer[i])
            un_min = aun_red_buffer[i]; // update signal min
        if (un_max < aun_red_buffer[i])
            un_max = aun_red_buffer[i]; // update signal max
    }
    un_prev_data = aun_red_buffer[i];
    // calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);

    printf("______>>>>>>> %s %d  un_min = %d un_max = %d\r\n", __FILE__, __LINE__, un_min, un_max);

    while (1)
    {
        // printf("1");
        i = 0;
        un_min = 0x3FFFF;
        un_max = 0;

        // dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
        for (i = 100; i < 500; i++)
        {
            aun_red_buffer[i - 100] = aun_red_buffer[i];
            aun_ir_buffer[i - 100] = aun_ir_buffer[i];

            // update the signal min and max
            if (un_min > aun_red_buffer[i])
                un_min = aun_red_buffer[i];
            if (un_max < aun_red_buffer[i])
                un_max = aun_red_buffer[i];
        }

        // take 100 sets of samples before calculating the heart rate.
        for (i = 400; i < 500; i++)
        {
            un_prev_data = aun_red_buffer[i - 1];
            while (1) // wait until the interrupt pin asserts
            {
                intkey_val = gpio_get_level(GPIO_KEY); // IoTGpioGetInputVal(5, &intkey_val);
                if (intkey_val == 0)
                {
                    break;
                }
                usleep(10);
            }
            max30102_FIFO_ReadBytes(REG_FIFO_DATA, temo_buf, 6);
            aun_red_buffer[i] = (long)((long)((long)temo_buf[0] & 0x03) << 16) | (long)temo_buf[1] << 8 | (long)temo_buf[2]; // Combine values to get the actual number
            aun_ir_buffer[i] = (long)((long)((long)temo_buf[3] & 0x03) << 16) | (long)temo_buf[4] << 8 | (long)temo_buf[5];  // Combine values to get the actual number

            if (aun_red_buffer[i] > un_prev_data)
            {
                f_temp = aun_red_buffer[i] - un_prev_data;
                f_temp /= (un_max - un_min);
                f_temp *= MAX_BRIGHTNESS;
                n_brightness -= (int)f_temp;
                if (n_brightness < 0)
                    n_brightness = 0;
            }
            else
            {
                f_temp = un_prev_data - aun_red_buffer[i];
                f_temp /= (un_max - un_min);
                f_temp *= MAX_BRIGHTNESS;
                n_brightness += (int)f_temp;
                if (n_brightness > MAX_BRIGHTNESS)
                    n_brightness = MAX_BRIGHTNESS;
            }
            // send samples and calculation result to terminal program through UART
            if (ch_hr_valid == 1 && n_heart_rate < 120) //**/ ch_hr_valid == 1 && ch_spo2_valid ==1 && n_heart_rate<120 && n_sp02<101
            {
                dis_hr = n_heart_rate;
                dis_spo2 = n_sp02;
            }
            else
            {
                dis_hr = 0;
                dis_spo2 = 0;
            }
            // printf("HR=%i, ", n_heart_rate);
            // printf("HRvalid=%i, ", ch_hr_valid);
            // printf("SpO2=%i, ", n_sp02);
            // printf("SPO2Valid=%i\r\n", ch_spo2_valid);
        }
        maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);

        // printf("______>>>>>>> %s %d  dis_hr = %d dis_spo2 = %d\r\n", __FILE__, __LINE__, dis_hr, dis_spo2);

        if (dis_hr == 0 && dis_spo2 == 0) //**dis_hr == 0 && dis_spo2 == 0
        {
            sprintf((char *)str, "HR:--- SpO2:--- "); //**HR:--- SpO2:---
        }
        else
        {
            sprintf((char *)str, "HR:%3d SpO2:%3d ", dis_hr, dis_spo2); //**HR:%3d SpO2:%3d

            f_dis_hr = dis_hr;
            f_dis_spo2 = dis_spo2;

            // printf("%s\r\n", str);
        }

        printf("%s\r\n", str);

        usleep(10000);
    }
}

void start_max30102_task(void)
{
    osThreadAttr_t attr;

    attr.name = "EnvironmentTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = 10240;
    attr.priority = osPriorityNormal;

    if (osThreadNew(EnvironmentTask, NULL, &attr) == NULL)
    {
        printf("[EnvironmentDemo] Falied to create EnvironmentTask!\n");
    }
}

OHOS_APP_RUN(start_max30102_task);
